using System;
using System.Collections.Generic;
using System.Diagnostics;
using FineCollection.Cont;
using FineCollection.Intf;



namespace FineCollection.Const
{


    public static partial class CC
    {

        #region EXTENSIONS


        /// <summary>
        /// Checks whether this item is contained in the specified set.
        /// </summary>
        /// <typeparam name="T">type of elements.</typeparam>
        /// <param name="item">element to check.</param>
        /// <param name="set">set of elements where to find.</param>
        /// <returns>the <paramref name="item"/> exists in the <paramref name="set"/>.</returns>
        public static bool In<T>(this T item, RSet<T> set)
        {
            return set != null && set.Contains(item);
        }


        /// <summary>
        /// Checks whether this item is contained in the specified collection.
        /// </summary>
        /// <typeparam name="T">type of elements.</typeparam>
        /// <param name="item">element to check.</param>
        /// <param name="collection">set of elements where to find.</param>
        /// <returns>the <paramref name="item"/> exists in the <paramref name="collection"/>.</returns>
        public static bool In<T>(this T item, RCollection<T> collection)
        {
            return collection != null && collection.Contains(item);
        }


        /// <summary>
        /// Performs the specified action for each item from the collection.
        /// </summary>
        /// <typeparam name="T">type of items.</typeparam>
        /// <param name="collection">collection to wich items perform action.</param>
        /// <param name="action">action to perform.</param>
        public static void ForEach<T>(this RCollection<T> collection, Action<T> action)
        {
            if (collection == null || collection.IsEmpty || action == null)
                return;

            // some optimization for ConstArray-based collections
            ConstArray<T> ca = collection as ConstArray<T>;
            if (ca != null)
            {
                ForEach(ca, action);
                return;
            }

            // for all other types of collections    
            foreach (T item in collection)
                action(item);
        }


        /// <summary>
        /// Performs the specified action for each item from the constant collection.
        /// </summary>
        /// <typeparam name="T">type of items.</typeparam>
        /// <param name="collection">collection to wich items perform action.</param>
        /// <param name="action">action to perform.</param>
        public static void ForEach<T>(this ConstArray<T> collection, Action<T> action)
        {
            if (collection == null || collection.IsEmpty || action == null)
                return;

            for (int i = 0; i < collection.count; i++)
                action(collection.arr[i]);
            return;
        }


        /// <summary>
        /// Makes a constant random access list by the given enumeration.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <returns>constant list.</returns>
        public static ConstList<T> ToConstList<T>(this IEnumerable<T> source)
        {
            if (source == null)
                return null;

            // fetch all
            var list = new ArrList<T>(1);
            var it = source.GetEnumerator();
            while (it.MoveNext())
                list.Add(it.Current);

            if (list.IsNotEmpty)
                return new ConstList<T>(1, list.ar, list.count, true);
            else
                return ConstZeroList<T>.Zero0;
        }

             
        /// <summary>
        /// Makes a constant random access sorted set by the given enumerator.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <returns>constant sorted set.</returns>
        public static RRandomSet<T> ToSortSet<T>(this IEnumerable<T> source)
            where T : IComparable<T>
        {
            return ToSortSet(source, 0);
        }


        /// <summary>
        /// Makes a constant random access sorted set by the given enumerator.
        /// </summary>
        /// <typeparam name="T">item type.</typeparam>
        /// <param name="source">enumeration to collect items from.</param>
        /// <param name="estimatedCount">estimated count of items will be produced by the given enumerator.</param>
        /// <returns>constant sorted set.</returns>
        public static ConstSortSet<T> ToSortSet<T>(this IEnumerable<T> source, int estimatedCount)
            where T : IComparable<T>
        {
            if (source == null)
                return null;

            ArrList<T> list = new ArrList<T>(1, estimatedCount);
            using (var enumerator = source.GetEnumerator())
                while (enumerator.MoveNext())
                    list.Add(enumerator.Current);

            if (list.IsNotEmpty)
                return new ConstSortSet<T>(1, list.ar, list.count, true, true, true);
            else
                return ConstZeroOrderSet<T>.Zero1;
        }


        /// <summary>
        /// Makes a constant sorted map from an array of items.
        /// </summary>
        /// <typeparam name="X">type of given item (that will be used for obtaining a key and a value).</typeparam>
        /// <typeparam name="K">type of map key.</typeparam>
        /// <typeparam name="V">type of map value.</typeparam>
        /// <param name="items">array with given items (mandatory but can be empty).</param>
        /// <param name="keyFunc">function for obtaining a key.</param>
        /// <param name="valueFunc">function for obtaining a value.</param>
        /// <returns>constant sorted map.</returns>
        public static ConstSortMap<K,V> ToSortMap<X,K,V> (this X[] items, Func<X,K> keyFunc, Func<X,V> valueFunc)
            where K: IComparable<K>
        {
            if (items == null)
                throw new ArgumentNullException("items");

            int n = items.Length;
            var entries = new ComparableMapEntry<K,V>[n];
            for (int i = 0; i < n; i++)
                entries[i] = new ComparableMapEntry<K,V>(keyFunc(items[i]), valueFunc(items[i]));

            return new ConstSortMap<K,V>(1, entries, n, true);
        }


        /// <summary>
        /// Makes a constant sorted map from a collection or enumeration of source items.
        /// </summary>
        /// <typeparam name="X">type of the source items.</typeparam>
        /// <typeparam name="K">type of map keys.</typeparam>
        /// <typeparam name="V">type of map associated values.</typeparam>
        /// <param name="source">source items.</param>
        /// <param name="keyFunc">key part producer.</param>
        /// <param name="valueFunc">associated value part producer.</param>
        /// <param name="estimatedCount">estimated count of items in the source.</param>
        /// <returns></returns>
        public static ConstSortMap<K,V> ToSortMap<X,K,V>(this IEnumerable<X> source, Func<X,K> keyFunc, Func<X,V> valueFunc, int estimatedCount)
            where K : IComparable<K>, IEquatable<K>
        {
            if (source == null)
                return null;

            // fetch all
            ArrList<ComparableMapEntry<K,V>> entries = new ArrList<ComparableMapEntry<K,V>>(1, estimatedCount);
            using (var enumerator = source.GetEnumerator())
                while (enumerator.MoveNext())
                {
                    X x = enumerator.Current;
                    var entry = new ComparableMapEntry<K, V>(keyFunc(x), valueFunc(x));
                    entries.Add(entry);
                }

            return new ConstSortMap<K,V>(1, entries.ar, entries.count, true);
        }


        #endregion




        #region UTILITY FUNCTIONS

        /// <summary>
        /// Checks whether the specified collection contains at least one item of type <c>T2</c>.
        /// </summary>
        /// <typeparam name="T2">type of item to check (must be a subclass of <c>T1</c>).</typeparam>
        /// <typeparam name="T1">type of collection's items.</typeparam>
        /// <param name="col">collection to check throw.</param>
        /// <returns>found or not.</returns>
        /// <deprecated/>
        public static bool HasItemOfType<T2,T1>(RCollection<T1> col)
            where T1: class
            where T2: class, T1
        {
            RIterator<T1> it = col.TakeIterator();
            while (it.Next())
                if (it.Current is T2)
                    return true;

            return false;
        }




        /// <summary>
        /// Compares two ordered sets for equality, with the default equator of the first set items.
        /// </summary>
        /// <typeparam name="T">supertype of all items.</typeparam>
        /// <typeparam name="T1">type of the first set items.</typeparam>
        /// <typeparam name="T2">type of the second set items, should be T1 or a subtype of T1.</typeparam>
        /// <param name="set1">the first set to compare.</param>
        /// <param name="set2">the second set to compare.</param>
        /// <returns>true if both sets are equal.</returns>
        public static bool AreEqual<T, T1, T2>(ROrderSet<T1> set1, ROrderSet<T2> set2)
            where T1: T
            where T2: T
        {
            var equator = EqualityComparer<T>.Default;

            int n1 = (set1 != null) ? set1.Count : 0,
                n2 = (set2 != null) ? set2.Count : 0;

            if (n1 == 0 && n2 == 0)
                return true;
            if (n1 != n2)
                return false;

            Debug.Assert(set1 != null && set2 != null);

            RIterator<T1> it1 = set1.TakeIterator();
            RIterator<T2> it2 = set2.TakeIterator();

            it1.Next();
            it2.Next();

            while (it1.Where < +1 && it2.Where < +1)
            {
                if (! equator.Equals(it1.Current, it2.Current))
                    return false;

                it1.Next();
                it2.Next();
            }

            if (it1.Where != it2.Where)
                return false;

            return true;
        }


        #endregion

    }


}